// Created: Dec 11, 2014
//
// Author: wjinwen.1988@gmail.com

#include "chi/config.h"

#include <stdlib.h>
#include <stdio.h>

#include <iostream>
#include <vector>


namespace chi {

static Config config_;

Config::Config() {}

Config::~Config() {
  for (StrMapIter it = config_map_.begin(); it != config_map_.end(); ++it) {
    delete it->second;
  }
}

Config& Config::instance() {
  return config_;
}

// 解析字符串
int Config::ParseValue(char* str, char* key, char* val) {
  char *p, *p1, *name, *value;

  if (!str)
    return -1;

  p = str;
  // 当前置空格
  while ((*p) == ' ' || (*p) == '\t' || (*p) == '\n') p++;
  p1 = p + strlen(p);
  // 去后置空格
  while (p1 > p) {
    p1--;
    if (*p1 == ' ' || *p1 == '\t' || *p1 == '\n') continue;
    p1++;
    break;
  }
  (*p1) = '\0';
  // 是注释行或空行
  if (*p == '#' || *p == '\0') return -1;
  p1 = strchr(str, '=');
  if (!p1) return -2;
  name = p;
  value = p1 + 1;
  while ((*(p1 -1)) == ' ') p1--;
  (*p1) = '\0'; // 替换'='为'\0'

  while ((*value) == ' ') value++;
  p = strchr(value, '#');
  if (!p) p = value + strlen(value);
  while ((*(p - 1)) <= ' ') p--;
  (*p) = '\0';
  if (name[0] == '\0') return -2;

  strcpy(key, name);
  strcpy(val, value);
  
  return 0;
}

// 是段名
char* Config::IsSectionName(char* str) {
  if (!str || strlen(str) <= 2 || (*str) != '[') return NULL;

  char* p = str + strlen(str);
  while ((*(p-1)) == ' ' || (*(p-1)) == '\t' || (*(p-1)) == '\n') p--;
  if (*(p-1) != ']') return NULL;
  *(p-1) = '\0';

  p = str + 1;
  while (*p) {
    if ((*p >= 'A' && *p <= 'Z') ||
        (*p >= 'a' && *p <= 'z') ||
        (*p >= '0' && *p <= '9') ||
        (*p == '_')) {
    } else {
      return NULL;
    }
    p++;
  }
  return str+1;
}

// 加载文件
int Config::Load(const char* filename) {
  FILE* fp;
  char key[128], value[4096], data[4096];
  int ret, line = 0;

  if ((fp = fopen(filename, "rb")) == NULL) {
    std::cerr << "Can't open file: " << filename << std::endl;
    return EXIT_FAILURE;
  }

  StrStrMap *m = NULL;
  while (fgets(data, 4096, fp)) {
    line++;
    char* name = IsSectionName(data);
    // 是段名
    if (name) {
      StrMapIter it = config_map_.find(name);
      if (it == config_map_.end()) {
        m = new StrStrMap();
        config_map_.insert(StrMap::value_type(name, m));
      } else {
        m = it->second;
      }
      continue;
    }
    ret = ParseValue(data, key, value);
    if (ret == -2) {
      std::cerr << "Parse error, Line: " << line << ", " << data << std::endl;
      fclose(fp);
      return EXIT_FAILURE;
    }
    if (ret < 0) continue;
    if (!m) {
      std::cerr << "section not found, Line: "
                << line << ", " << data << std::endl;
      fclose(fp);
      return EXIT_FAILURE;
    }
    StrStrMapIter it1 = m->find(key);
    if (it1 != m->end()) {
      it1->second += '\0';
      it1->second += value;
    } else {
      m->insert(StrStrMap::value_type(key, value));
    }
  }
  fclose(fp);
  return EXIT_SUCCESS;
}

int Config::Reload(const char* filename) {
  StrMapIter it = config_map_.begin();
  for (; it != config_map_.end(); ++it) {
    StrStrMap* ss_map = it->second;
    if (ss_map)
      delete ss_map;
  }
  config_map_.clear();

  return Load(filename);
}

// 取一个string
const char* Config::GetString(const char* section,
                              const std::string& key,
                              const char* d) {
  StrMapIter it = config_map_.find(section);
  if (it == config_map_.end()) {
    return d;
  }
  StrStrMapIter it1 = it->second->find(key);
  if (it1 == it->second->end()) {
    return d;
  }
  return it1->second.c_str();
}

// 取一string列表
std::vector<const char*> Config::GetStringList(const char* section,
                                               const std::string& key) {
  std::vector<const char*> ret;
  StrMapIter it = config_map_.find(section);
  if (it == config_map_.end()) {
    return ret;
  }
  StrStrMapIter it1 = it->second->find(key);
  if (it1 == it->second->end()) {
    return ret;
  }
  const char* data = it1->second.data();
  const char* p = data;
  for (int i = 0; i < (int)it1->second.size(); i++) {
    if (data[i] == '\0') {
      ret.push_back(p);
      p = data+i+1;
    }
  }
  ret.push_back(p);
  return ret;
}

// 取一个整数
int Config::GetInt(const char* section, const std::string& key, int d) {
  const char* str = GetString(section, key);
  if (!str)
    return d;
  
  return atoi(str);
}

// 取一int list
std::vector<int> Config::GetIntList(const char* section,
                                    const std::string& key) {
  std::vector<int> ret;
  StrMapIter it = config_map_.find(section);
  if (it == config_map_.end()) return ret;
  StrStrMapIter it1= it->second->find(key);
  if (it1 == it->second->end()) return ret;
  const char* data = it1->second.data();
  const char* p = data;
  for (int i = 0; i < (int)it1->second.size(); i++) {
    if (data[i] == '\0') {
      ret.push_back(atoi(p));
      p = data+i+1;
    }
  }
  ret.push_back(atoi(p));
  return ret;
}

// 取一section下所有的key
int Config::GetSectionKey(const char* section,
                          std::vector<std::string>& keys) {
  StrMapIter it = config_map_.find(section);
  if (it == config_map_.end()) {
    return 0;
  }
  StrStrMapIter it1;
  for (it1 = it->second->begin(); it1 != it->second->end(); ++it1) {
    keys.push_back(it1->first);
  }
  return (int)keys.size();
}

// 得到所有section的名字
int Config::GetSectionName(std::vector<std::string>& sections) {
  StrMapIter it;
  for (it = config_map_.begin(); it != config_map_.end(); ++it) {
    sections.push_back(it->first);
  }
  return (int)sections.size();
}

// ToString
std::string Config::ToString() {
  std::string result;
  StrMapIter it;
  StrStrMapIter it1;
  for (it = config_map_.begin(); it != config_map_.end(); ++it) {
    result += "[" + it->first + "]";
    for (it1 = it->second->begin(); it1 != it->second->end(); ++it1) {
      std::string s = it1->second.c_str();
      result += "    " + it1->first + " = " + s + "\n";
      if (s.size() != it1->second.size()) {
        char* data = (char*)it1->second.data();
        char* p = NULL;
        for (int i = 0; i < (int)it1->second.size(); i++) {
          if (data[i] != '\0') continue;
          if (p) result += "    " + it1->first + " = " + p + "\n";
          p = data+i+1;
        }
        if (p) result += "    " + it1->first + " = " + p + "\n";
      }
    }
  }
  result += "\n";
  return result;
}

}  // namespace chi

